package com.clei.utils;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * 加解密 util
 *
 * @author KIyA
 */
public class EncryptUtil {

    /**
     * AES_CBC
     */
    public static final String[] AES_CBC = {"AES", "AES/CBC/PKCS5Padding"};

    /**
     * AES_ECB
     */
    public static final String[] AES_ECB = {"AES", "AES"};

    /**
     * 3DES ECB 不需要向量  key的长度必须大于等于 3*8位
     * 3DES ECB直接用 DESede也行
     */
    private static final String[] THREE_DES_ECB = {"DESede", "DESede/ECB/PKCS5Padding"};
    private static final String[] THREE_DES_CBC = {"DESede", "DESede/CBC/PKCS5Padding"};

    /**
     * DES_ECB
     */
    private static final String[] DES_ECB = {"DES", "DES/ECB/PKCS5Padding"};

    /**
     * 3DES 加密
     *
     * @param bytes 要加密的数据
     * @param key   加密key
     * @return 加密后的数据
     */
    public static byte[] encrypt3DES(byte[] bytes, String key) {
        return encrypt(THREE_DES_ECB, bytes, key, null);
    }

    /**
     * 3DES 加密
     *
     * @param bytes  要加密的数据
     * @param key    加密key
     * @param vector 向量
     * @return 加密后的数据
     */
    public static byte[] encrypt3DES(byte[] bytes, String key, String vector) {
        return encrypt(THREE_DES_CBC, bytes, key, vector);
    }

    /**
     * DES 加密
     *
     * @param bytes  要加密的数据
     * @param key    加密key
     * @param vector 向量
     * @return 加密后的数据
     */
    public static byte[] encryptDES(byte[] bytes, String key, String vector) {
        return encrypt(DES_ECB, bytes, key, vector);
    }

    /**
     * AES 加密
     *
     * @param bytes 要加密的数据
     * @param key   加密key
     * @return 加密后的数据
     */
    public static byte[] encryptAES(byte[] bytes, String key) {
        return encrypt(AES_ECB, bytes, key, null);
    }

    /**
     * AES 加密
     *
     * @param bytes  要加密的数据
     * @param key    加密key
     * @param vector 向量
     * @return 加密后的数据
     */
    public static byte[] encryptAES(byte[] bytes, String key, String vector) {
        return encrypt(AES_CBC, bytes, key, vector);
    }

    /**
     * 3DES 解密
     *
     * @param bytes 要解密的数据
     * @param key   解密key
     * @return 解密后的数据
     */
    public static byte[] decrypt3DES(byte[] bytes, String key) {
        return decrypt(THREE_DES_ECB, bytes, key, null);
    }

    /**
     * 3DES 解密
     *
     * @param bytes  要解密的数据
     * @param key    解密key
     * @param vector 向量
     * @return 解密后的数据
     */
    public static byte[] decrypt3DES(byte[] bytes, String key, String vector) {
        return decrypt(THREE_DES_CBC, bytes, key, vector);
    }

    /**
     * DES 解密
     *
     * @param bytes  要解密的数据
     * @param key    解密key
     * @param vector 向量
     * @return 解密后的数据
     */
    public static byte[] decryptDES(byte[] bytes, String key, String vector) {
        return decrypt(DES_ECB, bytes, key, vector);
    }

    /**
     * AES 解密
     *
     * @param bytes 要解密的数据
     * @param key   解密key
     * @return 解密后的数据
     */
    public static byte[] decryptAES(byte[] bytes, String key) {
        return decrypt(AES_ECB, bytes, key, null);
    }

    /**
     * AES 解密
     *
     * @param bytes  要解密的数据
     * @param key    解密key
     * @param vector 向量
     * @return 解密后的数据
     */
    public static byte[] decryptAES(byte[] bytes, String key, String vector) {
        return decrypt(AES_CBC, bytes, key, vector);
    }

    /**
     * 加密
     *
     * @param algorithm 算法
     * @param bytes     要加密的数据
     * @param key       加密key
     * @param vector    向量
     * @return data
     */
    public static byte[] encrypt(String[] algorithm, byte[] bytes, String key, String vector) {
        byte[] res = null;
        try {
            SecretKey secretKey = new SecretKeySpec(key.getBytes(), algorithm[0]);
            // 加密
            Cipher cipher = Cipher.getInstance(algorithm[1]);
            if (StringUtil.isNotBlank(vector)) {
                IvParameterSpec iv = new IvParameterSpec(vector.getBytes());
                cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
            } else {
                cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            }
            res = cipher.doFinal(bytes);
        } catch (Exception e) {
            PrintUtil.log("encrypt error algorithm : {}, bytes : {}", algorithm[1], ByteUtil.toHex(bytes), e);
        }
        return res;
    }

    /**
     * 解密
     *
     * @param algorithm 算法
     * @param bytes     要解密的数据
     * @param key       解密key
     * @param vector    向量
     * @return data
     */
    public static byte[] decrypt(String[] algorithm, byte[] bytes, String key, String vector) {
        byte[] res = null;
        try {
            byte[] enCodeFormat = key.getBytes();
            SecretKey secretKey = new SecretKeySpec(enCodeFormat, algorithm[0]);
            // 解密
            Cipher cipher = Cipher.getInstance(algorithm[1]);
            if (StringUtil.isNotBlank(vector)) {
                IvParameterSpec iv = new IvParameterSpec(vector.getBytes());
                cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
            } else {
                cipher.init(Cipher.DECRYPT_MODE, secretKey);
            }
            res = cipher.doFinal(bytes);
        } catch (Exception e) {
            PrintUtil.log("decrypt error algorithm : {}, bytes : {}", algorithm[1], ByteUtil.toHex(bytes), e);
        }
        return res;
    }
}
